Vue 2:虚拟 DOM 与 diff
同一话题集中阐述,便于对照阅读。
一、为何需要虚拟 DOM 与 diff
直接操作真实 DOM(创建、删除、移动)成本高,易触发重排/重绘。
Vue 先在内存维护 VNode 树,数据变更后生成新 VNode,与旧 VNode做 diff,只对差异部分 patch 到真实 DOM,从而减少 DOM 操作次数。
核心:不是为了一句「快」,而是为了可控的批量更新与跨平台抽象。
二、虚拟 DOM 是什么
VNode 是用 JS 对象描述节点类型、属性、子节点等的树形结构;首次渲染由渲染函数生成 VNode,再 patch 成真实 DOM。
好处归纳
- 性能:合并更新、最小化 DOM 写入。
- 跨平台:同一套 VNode 思路可对接不同渲染后端。
- 开发体验:声明式模板,多数场景不必手写 DOM API。
工作流程
- 渲染函数执行 → 新 VNode 树。
- patch(oldVnode, vnode):比对新旧差异。
- 将差异应用到真实 DOM。
与「只用新 VNode 全量替换真实 DOM」对比
若每次整树替换真实 DOM,稍有改动也代价巨大;diff 的意义在于复用仍有效的真实节点,只做增量变更。
三、Vue 2 diff 策略要点
关键词
- 同层比较:不大面积跨层级对比(跨层移动常被视为删除+新建)。
- 双端(双指针):子节点为数组时,从首尾向中间四种配对尝试复用。
- key:列表项身份标识,决定能否移动而非重建。
sameVnode / patchVnode
- 先判断是否 sameVnode(标签、
key、注释节点等语义一致)。 - 不同则替换;相同则 patchVnode,更新属性与子节点。
双端比较直觉
四个索引:旧头、旧尾、新头、新尾;四种快捷匹配失败后再用 key → 下标映射找可复用节点;剩余节点删除或新建。
复杂度
粗暴的两棵树比对可达更高阶;Vue 2 通过同层 + key + 双端把常见列表场景控制在近似 O(n)(n 为参与比较的节点规模)。
四、diff 时机
- 首次渲染:生成 VNode 并挂载,不与「上一帧」比对(无旧树)。
- 更新:响应式触发组件渲染函数 → 新 VNode 与上一次保留的旧 VNode比对。
与异步队列的关系
多次数据修改可能在一次 nextTick 内合并为一次渲染与 patch;patch 过程中仍是递归对比并逐步更新 DOM(与「队列合并」不在同一层级)。
五、Vue 2 vs React diff(简述)
常见对比:Vue 2 列表双端试探 vs React 基于 key 的映射与单向扫描;各有取舍,可从「减少 DOM 移动」角度回答即可。
六、vue2 diff 算法.md 精简对照
| Vue 2 | Vue 3 | React | |
|---|---|---|---|
| 列表思路 | 双端比较 | 最长递增子序列 等优化 | 单向扫描 + key 映射 |
详情见 Vue 3 合并文档中的跨框架对比章节。
七、列表优化要点
- 稳定唯一 key(如业务 id)。
- 避免
v-for同行使用v-if(Vue 2 尤其吃亏)。 - 长列表:分页、虚拟滚动。
- 大块静态内容:减少无谓的响应式拆分(与编译优化相关主要在 Vue 3)。
下一章:组件通信·缓存·Vuex·权限。
